package com.melodyframe.stage.core;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import com.melodyframe.common.context.Context;
import com.melodyframe.common.tools.javax.SortedList;
import com.melodyframe.stage.core.channel.Channel;
import com.melodyframe.stage.core.conductor.CommonConductor;
import com.melodyframe.stage.core.conductor.Conductor;
import com.melodyframe.stage.core.constants.CoreContextConstants;
import com.melodyframe.stage.core.performer.Filter;
import com.melodyframe.stage.core.performer.Performer;
import com.melodyframe.stage.core.renderer.CommonViewRenderer;
import com.melodyframe.stage.core.renderer.View;
import com.melodyframe.stage.core.renderer.ViewRenderer;

public class CommonStage implements Stage {
	private Map<String, Stage>		namespaces;
	private Map<String, Performer>	performers;
	private Collection<Filter>		filters;
	private Conductor<CommonStage>	conductor;
	private ViewRenderer			renderer;

	@Override
	public View perform(String path, Context context) {
		context.put(CoreContextConstants.ACTION,
				CoreContextConstants.EMPTY_ACTION_VALUE);
		if (null != this.namespaces) {
			int posSplit = path.indexOf("/", 1);
			if (posSplit > 1) {
				String subName = path.substring(1, posSplit);
				if (this.namespaces.containsKey(subName)) {
					String subPath = path.substring(posSplit);
					return this.namespaces.get(subName).perform(subPath,
							context);
				}
			}
		}
		if (null == this.conductor) {// Default conductor
			this.conductor = new CommonConductor();
		}
		Channel channel = this.conductor.createChannel(path, this);
		if (null != channel) {
			channel.perform(context);
		}
		if (null == this.renderer) {// Default renderer
			this.renderer = new CommonViewRenderer();
		}
		String renderPath = path;
		if (context.containsKey(CoreContextConstants.ACTION)) {
			Object actionObj = context.get(CoreContextConstants.ACTION);
			if (null != actionObj) {
				String action = actionObj.toString();
				if (!CoreContextConstants.EMPTY_ACTION_VALUE.equals(action)) {
					renderPath += "_" + action;
				}
			}
		}
		return renderer.render(renderPath, context);

	}

	public Map<String, Stage> getNamespaces() {
		return this.namespaces;
	}

	public void setNamespaces(Map<String, Stage> namespaces) {
		this.namespaces = namespaces;
	}

	public void addNamespace(String namespace, Stage stage) {
		if (null == this.namespaces) {
			this.namespaces = new HashMap<>();
		}
		this.awareStage(stage);
		this.namespaces.put(namespace, stage);
	}

	public Map<String, Performer> getPerformers() {
		return this.performers;
	}

	public void setPerformers(Map<String, Performer> performers) {
		for (String path : performers.keySet()) {
			this.addPerformer(path, performers.get(path));
		}
	}

	public void addPerformer(String path, Performer performer) {
		if (null == this.performers) {
			this.performers = new HashMap<String, Performer>();
		}
		this.awareStage(performer);
		this.performers.put(path, performer);

	}

	public Collection<Filter> getFilters() {
		return this.filters;
	}

	public void setFilters(Collection<Filter> filters) {
		for (Filter filter : filters) {
			this.addFilter(filter);
		}
	}

	public void addFilter(Filter filter) {
		if (null == this.filters) {
			this.filters = new SortedList<>(new Comparator<Filter>() {
				@Override
				public int compare(Filter filter, Filter anotherFilter) {
					Integer order = filter.getOrder();
					return order.compareTo(anotherFilter.getOrder());
				}
			});
		}
		this.awareStage(filter);
		this.filters.add(filter);
	}

	public void addFilter(int order, Performer performer) {
		this.addFilter(new Filter(order, performer));
	}

	public Conductor<CommonStage> getConductor() {
		return conductor;
	}

	public void setConductor(Conductor<CommonStage> conductor) {
		this.awareStage(conductor);
		this.conductor = conductor;
	}

	public ViewRenderer getRenderer() {
		return renderer;
	}

	public void setRenderer(ViewRenderer renderer) {
		this.awareStage(renderer);
		this.renderer = renderer;
	}

	private void awareStage(Object object) {
		if (null == object) {
			return;
		}
		if (object instanceof StageAware) {
			((StageAware) object).setStage(this);
		}

	}
}
